<html>
 <head>
  <meta charset="UTF-8">
 </head>
 <body>
  <h1 data-lake-id="PAuuf" id="PAuuf"><span data-lake-id="u0aa47e87" id="u0aa47e87">典型回答</span></h1>
  <p data-lake-id="u94612475" id="u94612475"><br></p>
  <p data-lake-id="u3c567081" id="u3c567081"><span data-lake-id="uf5129d97" id="uf5129d97">Kafka作为一个消息中间件，他需要结合消息生产者和消费者一起才能工作，一次消息发送包含以下是三个过程：</span></p>
  <p data-lake-id="u1c9a27ca" id="u1c9a27ca"><span data-lake-id="u2c5f7347" id="u2c5f7347">​</span><br></p>
  <p data-lake-id="ud7ffc0d0" id="ud7ffc0d0"><span data-lake-id="u69a29998" id="u69a29998">1）Producer 端发送消息给 Kafka Broker 。</span></p>
  <p data-lake-id="uff919855" id="uff919855"><span data-lake-id="ub6e7a5a7" id="ub6e7a5a7">2）Kafka Broker 将消息进行同步并持久化数据。</span></p>
  <p data-lake-id="ufe1fb0e9" id="ufe1fb0e9"><span data-lake-id="u6641f91b" id="u6641f91b">3）Consumer 端从Kafka Broker 将消息拉取并进行消费。</span></p>
  <p data-lake-id="ufd392515" id="ufd392515"><span data-lake-id="u17f215a1" id="u17f215a1">​</span><br></p>
  <p data-lake-id="u22360f69" id="u22360f69"><span data-lake-id="uafee4eb9" id="uafee4eb9">​</span><strong><span data-lake-id="ud8d78cee" id="ud8d78cee">Kafka只对已提交的消息做最大限度的持久化保证不丢失，但是没办法保证100%。</span></strong></p>
  <p data-lake-id="u34f51a13" id="u34f51a13"><strong><span data-lake-id="u63b19e54" id="u63b19e54">​</span></strong><br></p>
  <p data-lake-id="ucc8eeceb" id="ucc8eeceb"><span data-lake-id="u3dbdf165" id="u3dbdf165">​</span><br></p>
  <p data-lake-id="u4f909758" id="u4f909758"><span data-lake-id="u81c15b0f" id="u81c15b0f">但是，Kafka还是提供了很多机制来保证消息不丢失的。要想知道Kafka如何保证消息不丢失，需要从生产者、消费者以及kafka集群三个方面来分析。</span></p>
  <p data-lake-id="ua237441d" id="ua237441d"><br></p>
  <h3 data-lake-id="QfAYa" id="QfAYa"><span data-lake-id="u313b1e10" id="u313b1e10">Producer</span></h3>
  <p data-lake-id="u43749d81" id="u43749d81"><br></p>
  <p data-lake-id="u0ebb2a74" id="u0ebb2a74"><span data-lake-id="ubf343fc3" id="ubf343fc3">消息的生产者端，最怕的就是消息发送给Kafka集群的过程中失败，所以，我们需要有机制来确保消息能够发送成功，但是，因为存在网络问题，所以基本没有什么办法可以保证一次消息一定能成功。</span></p>
  <p data-lake-id="u79a3b28b" id="u79a3b28b"><span data-lake-id="u63674071" id="u63674071">​</span><br></p>
  <p data-lake-id="ufe09872d" id="ufe09872d"><span data-lake-id="ub900aa31" id="ub900aa31">所以，就需要有一个确认机制来告诉生产者这个消息是否有发送成功，如果没成功，需要重新发送直到成功。</span></p>
  <p data-lake-id="u47b3f46a" id="u47b3f46a"><span data-lake-id="ueaa67ca8" id="ueaa67ca8">​</span><br></p>
  <p data-lake-id="u9c0d8506" id="u9c0d8506"><span data-lake-id="u6cf73203" id="u6cf73203">我们通常使用Kafka发送消息的时候，通常使用的</span><code data-lake-id="u66b37599" id="u66b37599"><span data-lake-id="u42c09274" id="u42c09274">producer.send(msg)</span></code><span data-lake-id="uca925e1b" id="uca925e1b">其实是一种</span><strong><span data-lake-id="u411329cc" id="u411329cc">异步发送</span></strong><span data-lake-id="ud71aada1" id="ud71aada1">，发送消息的时候，方法会立即返回，但是并不代表消息一定能发送成功。（</span><code data-lake-id="u419e079f" id="u419e079f"><span data-lake-id="u582d8c24" id="u582d8c24">producer.send(msg).get()</span></code><span data-lake-id="u1c61e7fa" id="u1c61e7fa"> 是同步等待返回的。）</span></p>
  <p data-lake-id="u5b5c7f9a" id="u5b5c7f9a"><span data-lake-id="u38dfffa8" id="u38dfffa8">​</span><br></p>
  <p data-lake-id="u66a1109c" id="u66a1109c"><span data-lake-id="ucaf3a767" id="ucaf3a767">那么，为了保证消息不丢失，通常会建议</span><strong><span data-lake-id="u5547dfda" id="u5547dfda">使用</span></strong><code data-lake-id="uec470614" id="uec470614"><strong><span data-lake-id="uebd453f3" id="uebd453f3">producer.send(msg, callback)</span></strong></code><strong><span data-lake-id="u96da4011" id="u96da4011">方法</span></strong><span data-lake-id="u695c9966" id="u695c9966">，这个方法支持传入一个callback，我们可以在消息发送时进行重试。</span></p>
  <p data-lake-id="u8ca29809" id="u8ca29809"><span data-lake-id="ua69480ed" id="ua69480ed">​</span><br></p>
  <p data-lake-id="uf1d95685" id="uf1d95685"><span data-lake-id="uc48f568f" id="uc48f568f">同时，我们也可以通过给producer设置一些参数来提升发送成功率：</span></p>
  <p data-lake-id="u65eb98a6" id="u65eb98a6"><span data-lake-id="u10a36c31" id="u10a36c31">​</span><br></p>
  <pre lang="java"><code>
acks=-1 // 表示 Leader 和 Follower 都接收成功时确认；可以最大限度保证消息不丢失，但是吞吐量低。
retries=3 // 生产端的重试次数
retry.backoff.ms = 300  //消息发送超时或失败后，间隔的重试时间
</code></pre>
  <p data-lake-id="ub83bf5c0" id="ub83bf5c0"><span data-lake-id="u03f150ab" id="u03f150ab">​</span><br></p>
  <blockquote data-lake-id="u1e36c0aa" id="u1e36c0aa">
   <p data-lake-id="u50b2584b" id="u50b2584b"><span data-lake-id="uc72fa3ff" id="uc72fa3ff">acks = 0: 表示Producer请求立即返回，不需要等待Leader的任何确认。这种方案有最高的吞吐率，但是不保证消息是否真的发送成功。</span></p>
   <p data-lake-id="udbdd189e" id="udbdd189e"><span data-lake-id="u4521e043" id="u4521e043">acks = -1: 表示分区Leader必须等待消息被成功写入到所有的ISR副本(同步副本)中才认为Producer请求成功。这种方案提供最高的消息持久性保证，但是理论上吞吐率也是最差的。</span></p>
   <p data-lake-id="uff12c066" id="uff12c066"><span data-lake-id="ud045ab86" id="ud045ab86">acks = 1: 表示Leader副本必须应答此Producer请求并写入消息到本地日志，之后Producer请求被认为成功。如果此时Leader副本应答请求之后挂掉了，消息会丢失。这个方案，提供了不错的持久性保证和吞吐。</span></p>
  </blockquote>
  <h3 data-lake-id="x8qHR" id="x8qHR"><span data-lake-id="u9cd9228d" id="u9cd9228d">Broker</span></h3>
  <p data-lake-id="u1c129156" id="u1c129156"><br></p>
  <p data-lake-id="u4db32495" id="u4db32495"><span data-lake-id="ua59ee056" id="ua59ee056">Kafka的集群有一些机制来保证消息的不丢失，比如复制机制、持久化存储机制以及ISR机制。</span></p>
  <p data-lake-id="u921fb20a" id="u921fb20a"><br></p>
  <ul list="u1fb00057">
   <li fid="ude01ee1e" data-lake-id="u13e487ee" id="u13e487ee"><strong><span data-lake-id="u9f2de0e2" id="u9f2de0e2">持久化存储</span></strong><span data-lake-id="u9ca9a07c" id="u9ca9a07c">：Kafka使用持久化存储来存储消息。这意味着消息在写入Kafka时将被写入磁盘，这种方式可以防止消息因为节点宕机而丢失。</span></li>
   <li fid="ude01ee1e" data-lake-id="u1cf043f1" id="u1cf043f1"><strong><span data-lake-id="ub634c382" id="ub634c382">ISR复制机制</span></strong><span data-lake-id="u5da8a5c7" id="u5da8a5c7">：Kafka使用ISR机制来确保消息不会丢失，Kafka使用复制机制来保证数据的可靠性。每个分区都有多个副本，副本可以分布在不同的节点上。当一个节点宕机时，其他节点上的副本仍然可以提供服务，保证消息不丢失。</span></li>
  </ul>
  <p data-lake-id="uc04b97b8" id="uc04b97b8"><br></p>
  <p data-lake-id="u9dc2d929" id="u9dc2d929"><span data-lake-id="u2e7a8743" id="u2e7a8743">在服务端，也有一些参数配置可以调节来避免消息丢失：</span></p>
  <p data-lake-id="ub483d2a6" id="ub483d2a6"><span data-lake-id="u1995d2d4" id="u1995d2d4">​</span><br></p>
  <pre lang="java"><code>
replication.factor //表示分区副本的个数，replication.factor &gt;1 当leader 副本挂了，follower副本会被选举为leader继续提供服务。
min.insync.replicas //表示 ISR 最少的副本数量，通常设置 min.insync.replicas &gt;1，这样才有可用的follower副本执行替换，保证消息不丢失
unclean.leader.election.enable = false //是否可以把非 ISR 集合中的副本选举为 leader 副本。
</code></pre>
  <h3 data-lake-id="MhKNI" id="MhKNI"><span data-lake-id="u26aada9b" id="u26aada9b">Consumer</span></h3>
  <p data-lake-id="uf0fddb08" id="uf0fddb08"><br></p>
  <p data-lake-id="u89e93c60" id="u89e93c60"><span data-lake-id="uab45e4d4" id="uab45e4d4">作为Kafka的消费者端，只需要确保投递过来的消息能正常消费，并且不会胡乱的提交偏移量就行了。</span></p>
  <p data-lake-id="ub63a7f6f" id="ub63a7f6f"><span data-lake-id="u01f61104" id="u01f61104">​</span><br></p>
  <p data-lake-id="u6d0824dd" id="u6d0824dd"><span data-lake-id="u067e5e1f" id="u067e5e1f">Kafka消费者会跟踪每个分区的</span><strong><span data-lake-id="u13e8cb5b" id="u13e8cb5b">偏移量</span></strong><span data-lake-id="u76f6d3d6" id="u76f6d3d6">，消费者每次消费消息时，都会将偏移量向后移动。当消费者宕机或者不可用时，Kafka会将该消费者所消费的分区的偏移量保存下来，下次该消费者重新启动时，可以从上一次的偏移量开始消费消息。</span></p>
  <p data-lake-id="u284e2d6f" id="u284e2d6f"><span data-lake-id="ueeb98882" id="ueeb98882">​</span><br></p>
  <p data-lake-id="u1a0e26cd" id="u1a0e26cd"><span data-lake-id="u6888d281" id="u6888d281">另外，Kafka消费者还可以组成</span><strong><span data-lake-id="u30f539fb" id="u30f539fb">消费者组</span></strong><span data-lake-id="ub3903e82" id="ub3903e82">，每个消费者组可以同时消费多个分区。当一个消费者组中的消费者宕机或者不可用时，其他消费者仍然可以消费该组的分区，保证消息不丢失。</span></p>
  <p data-lake-id="u342cf004" id="u342cf004"><span data-lake-id="ud327e0c9" id="ud327e0c9">​</span><br></p>
  <p data-lake-id="u1bacbca7" id="u1bacbca7"><span data-lake-id="uc7134e01" id="uc7134e01">为了保证消息不丢失，建议使用手动提交偏移量的方式，避免拉取了消息以后，业务逻辑没处理完，提交偏移量后但是消费者挂了的问题：</span></p>
  <p data-lake-id="u54ac5c5f" id="u54ac5c5f"><br></p>
  <pre lang="java"><code>
enable.auto.commit=false
</code></pre>
  <p data-lake-id="u711c17db" id="u711c17db"><br></p>
  <p data-lake-id="uc6976502" id="uc6976502"><span data-lake-id="u93e44857" id="u93e44857">​</span><br></p>
  <p data-lake-id="u86d70446" id="u86d70446"><span data-lake-id="u3637c551" id="u3637c551">​</span><br></p>
  <p data-lake-id="u4566b751" id="u4566b751"><span data-lake-id="ue949d59a" id="ue949d59a">​</span><br></p>
  <p data-lake-id="uba2085fc" id="uba2085fc"><span data-lake-id="ubc2c7e29" id="ubc2c7e29">​</span><br></p>
  <p data-lake-id="ubbab638b" id="ubbab638b"><span data-lake-id="ufd15a9ea" id="ufd15a9ea">​</span><br></p>
 </body>
</html>